import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import ttkbootstrap as ttk
from PIL import Image, ExifTags, ImageTk, ImageFilter, ImageEnhance, ImageChops, ImageDraw, ImageFont
import os
from pathlib import Path
import threading
import queue
import rawpy
import numpy as np
from utils_image import load_raw_image, safe_open_image, extract_raw_thumbnail
from calc_utils import convert_to_pixels, convert_from_pixels, calculate_optimal_dpi_for_image
from processing_mixin import ProcessingMixin
from analysis_mixin import AnalysisMixin
import dpi_size_mixin
import batch_ratio_mixin
import std_size_mixin
from size_mixin import SizeMixin


# 尝试导入pillow-heif以支持HEIC格式
try:
    from pillow_heif import register_heif_opener

    register_heif_opener()
    HEIC_SUPPORTED = True
    print("HEIC格式支持已启用")
except ImportError:
    HEIC_SUPPORTED = False
    print("警告：未安装pillow-heif，HEIC格式将不被支持。请运行：pip install pillow-heif")

# 增加Pillow的最大像素限制，解决"decompression bomb DOS attack"错误
Image.MAX_IMAGE_PIXELS = 1000000000  # 设置为大约10亿像素，足够处理大多数大图片


class ImageProcessorApp(ProcessingMixin, AnalysisMixin,
                        dpi_size_mixin.DpiSizeMixin,
                        batch_ratio_mixin.BatchRatioMixin,
                        std_size_mixin.StdSizeMixin,
                        SizeMixin):
    def __init__(self, root):
        # 初始化根窗口
        self.root = root
        self.root.title("图像处理工具")

        # 获取屏幕尺寸
        self.screen_width = self.root.winfo_screenwidth()
        self.screen_height = self.root.winfo_screenheight()

        # 计算缩放比例，用于在小屏幕上适当缩小界面元素
        self.width_scale_factor = min(1.0, self.screen_width / 1920)
        self.height_scale_factor = min(1.0, self.screen_height / 1080)
        self.scale_factor = min(self.width_scale_factor, self.height_scale_factor)

        # 绑定窗口大小变化事件
        self.root.bind("<Configure>", self.on_window_resize)

        # 设置固定基准DPI值，用于计算缩放比例
        self.base_dpi = 96.0  # 标准基准DPI值

        # 创建缩放基准单位，所有尺寸将基于此单位计算
        self.base_unit = 10  # 基础单位值

        # 设置固定的控件参数和间距，严格使用scale方法确保比例一致
        self.padx_size = self.scale(0.5 * self.base_unit)
        self.pady_size = self.scale(0.5 * self.base_unit)
        self.button_padding = self.scale(0.5 * self.base_unit)
        self.small_padx = self.scale(0.5 * self.base_unit)
        self.medium_padx = self.scale(1.0 * self.base_unit)
        self.large_padx = self.scale(2.0 * self.base_unit)

        # 设置精确缩放的初始窗口尺寸
        self.window_width = self.scale(50 * self.base_unit)
        self.window_height = self.scale(30 * self.base_unit)

        # 设置窗口初始大小
        self.root.geometry(f"{self.window_width}x{self.window_height}")

        # 创建主题并设置颜色
        self.style = ttk.Style(theme="flatly")

        # 检查HEIC支持并显示提示
        if not HEIC_SUPPORTED:
            self.root.after(1000, self.show_heic_warning)

        # 修改主题颜色
        self.style.colors.primary = "#E6B3E6"
        self.style.colors.secondary = "#E6B3E6"
        self.style.colors.success = "#E6B3E6"
        self.style.colors.info = "#E6B3E6"
        self.style.colors.warning = "#E6B3E6"
        self.style.colors.danger = "#E6B3E6"

        # 设置全局颜色
        self.style.configure(".",
                             background="white",
                             foreground="black")

        # 使用严格缩放的字体大小
        self.font_size = self.scale(1.1 * self.base_unit)
        self.style.configure(".", font=("", self.font_size))

        # 定义缩放后的标准控件尺寸
        self.entry_width = self.scale(6.0 * self.base_unit)
        self.small_entry_width = self.scale(1.0 * self.base_unit)
        self.tiny_entry_width = self.scale(0.5 * self.base_unit)
        self.treeview_height = self.scale(0.4 * self.base_unit)
        self.padding = self.scale(2.0 * self.base_unit)
        self.small_padding = self.scale(1.0 * self.base_unit)

        # 创建主滚动框架
        self.main_canvas = ttk.Canvas(self.root)
        self.scrollable_frame = ttk.Frame(self.main_canvas)

        self.scrollable_frame.bind(
            "<Configure>",
            lambda e: self.main_canvas.configure(scrollregion=self.main_canvas.bbox("all"))
        )

        self.main_canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")

        # 创建主框架
        self.main_frame = ttk.Frame(self.scrollable_frame, padding=str(self.padding))
        self.main_frame.pack(fill=tk.BOTH, expand=True)

        # 选择模式
        self.mode_frame = ttk.LabelFrame(self.main_frame, text="选择模式", padding=str(self.small_padding))
        self.mode_frame.pack(fill=tk.X, pady=(0, self.small_padding))

        self.mode_var = tk.StringVar(value="folder")
        self.folder_radio = ttk.Radiobutton(
            self.mode_frame,
            text="选择图片所在文件夹(包含所有子文件夹)",
            variable=self.mode_var,
            value="folder",
            command=self.update_source_frame,
            bootstyle="danger-round-toggle"
        )
        self.folder_radio.pack(side=tk.LEFT, padx=(0, self.large_padx))

        self.files_radio = ttk.Radiobutton(
            self.mode_frame,
            text="选择图片(按住ctrl可多选)",
            variable=self.mode_var,
            value="files",
            command=self.update_source_frame,
            bootstyle="danger-round-toggle"
        )
        self.files_radio.pack(side=tk.LEFT)

        # 初始化路径变量
        self.source_path = tk.StringVar()
        self.dest_path = tk.StringVar()

        # 定义进度条颜色
        self.progress_bg_color = "#F0F0F0"  # 浅灰色背景
        self.progress_bar_color = "#E6B3E6"  # 粉色填充
        self.text_color_dark = "black"  # 深色文字（用于灰色背景）
        self.text_color_light = "white"  # 浅色文字（用于粉色背景）

        # 创建源文件框架
        self.source_frame = ttk.LabelFrame(self.main_frame, text="加载文件路径", padding=str(self.small_padding))
        self.source_frame.pack(fill=tk.X, pady=(0, self.small_padding))

        # 使用pack布局，确保按钮先放置在右侧
        self.source_btn = ttk.Button(
            self.source_frame,
            text="浏览",
            command=self.select_source,
            style="Accent.TButton"
        )
        self.source_btn.pack(side=tk.RIGHT)

        # 然后创建源路径输入框，让它填充剩余空间
        self.source_entry = ttk.Entry(self.source_frame, textvariable=self.source_path, width=0)
        self.source_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))

        # 创建文件列表但不立即显示（将在update_source_frame中根据模式选择显示）
        self.files_list = ttk.Treeview(self.source_frame, columns=("path",), show="", height=self.treeview_height)
        self.files_list.pack(fill=tk.X, expand=True)
        self.files_list.pack_forget()  # 默认隐藏

        # 创建目标路径框架
        self.dest_frame = ttk.LabelFrame(self.main_frame, text="导出图片位置", padding=str(self.small_padding))
        self.dest_frame.pack(fill=tk.X, pady=(0, self.small_padding))

        # 同样使用pack布局，确保按钮先放置在右侧
        self.dest_btn = ttk.Button(
            self.dest_frame,
            text="浏览",
            command=self.select_dest,
            style="Accent.TButton"
        )
        self.dest_btn.pack(side=tk.RIGHT)

        # 然后创建目标路径输入框，让它填充剩余空间
        self.dest_entry = ttk.Entry(self.dest_frame, textvariable=self.dest_path, width=0)
        self.dest_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))

        # 参数设置区域
        self.params_frame = ttk.LabelFrame(self.main_frame, text="参数设置", padding=str(self.small_padding))
        self.params_frame.pack(fill=tk.BOTH, expand=True, pady=(0, self.small_padding))

        # DPI设置
        self.dpi_control_frame = ttk.Frame(self.params_frame)
        self.dpi_control_frame.pack(fill=tk.X, pady=(0, self.scale(0.5 * self.base_unit)))

        self.dpi_enabled = tk.BooleanVar(value=False)
        self.dpi_checkbox = ttk.Checkbutton(
            self.dpi_control_frame,
            text="修改DPI(当不勾选时，保持原图DPI不变)",
            variable=self.dpi_enabled,
            command=self.update_dpi_state,
            bootstyle="danger-round-toggle"
        )
        self.dpi_checkbox.pack(side=tk.LEFT)

        # 添加重新采样复选框
        self.resampling_enabled = tk.BooleanVar(value=True)  # 默认开启重新采样
        self.resampling_checkbox = ttk.Checkbutton(
            self.dpi_control_frame,
            text="重新采样(仅在保持原图比例的情况下才可以取消勾选)",
            variable=self.resampling_enabled,
            command=self.update_resampling_state,
            bootstyle="danger-round-toggle"
        )
        self.resampling_checkbox.pack(side=tk.LEFT, padx=self.scale(1.0 * self.base_unit))

        self.dpi_frame = ttk.Frame(self.params_frame)
        self.dpi_frame.pack(fill=tk.X, pady=(0, self.scale(1.0 * self.base_unit)))

        ttk.Label(self.dpi_frame, text="DPI数值:").pack(side=tk.LEFT)
        self.dpi_var = tk.StringVar(value="")
        self.dpi_entry = ttk.Entry(self.dpi_frame, textvariable=self.dpi_var, width=self.small_entry_width)
        self.dpi_entry.pack(side=tk.LEFT, padx=self.scale(0.5 * self.base_unit))
        self.dpi_entry.configure(state="disabled")

        # 在创建DPI相关控件后添加
        self.dpi_enabled.set(False)  # 默认不启用DPI修改
        self.selected_files = []  # 初始化选择的文件列表为空

        # 在__init__方法中，替换原有的尺寸设置代码
        # 尺寸设置区域
        self.size_control_frame = ttk.LabelFrame(self.params_frame, text="尺寸设置", padding=str(self.small_padding))
        self.size_control_frame.pack(fill=tk.X, pady=(0, self.small_padding))

        # 添加尺寸修改控制复选框和标准尺寸选择框的容器
        self.size_control_top_frame = ttk.Frame(self.size_control_frame)
        self.size_control_top_frame.pack(fill=tk.X, pady=(0, self.scale(0.5 * self.base_unit)))

        # 添加尺寸修改控制复选框
        self.size_enabled = tk.BooleanVar(value=False)
        self.size_checkbox = ttk.Checkbutton(
            self.size_control_top_frame,
            text="修改尺寸(当不勾选时，保持原图尺寸不变)",
            variable=self.size_enabled,
            command=self.update_size_state,
            bootstyle="danger-round-toggle"
        )
        self.size_checkbox.pack(side=tk.LEFT)

        # 标准尺寸选择功能（放在修改尺寸复选框的右边）
        self.standard_size_frame = ttk.Frame(self.size_control_top_frame)
        self.standard_size_frame.pack(side=tk.RIGHT)

        ttk.Label(self.standard_size_frame, text="标准尺寸:").pack(side=tk.LEFT,
                                                                   padx=(self.scale(2.0 * self.base_unit), 0))

        # 标准尺寸选项（英寸）
        self.standard_sizes = {
            "自定义": (0, 0),
            "1寸": (1.0, 1.5),
            "2寸": (1.5, 2.0),
            "3寸": (2.5, 3.5),
            "4寸": (3.0, 4.0),
            "5寸": (3.5, 5.0),
            "6寸": (4.0, 6.0),
            "7寸": (5.0, 7.0),
            "8寸": (6.0, 8.0),
            "10寸": (8.0, 10.0),
            "12寸": (10.0, 12.0)
        }

        self.standard_size_var = tk.StringVar(value="自定义")
        self.standard_size_combo = ttk.Combobox(
            self.standard_size_frame,
            textvariable=self.standard_size_var,
            values=list(self.standard_sizes.keys()),
            state="readonly",
            width=8
        )
        self.standard_size_combo.pack(side=tk.LEFT, padx=self.small_padx)
        self.standard_size_combo.bind("<<ComboboxSelected>>", self.on_standard_size_selected)

        # 标准尺寸应用状态
        self.standard_size_applied = False

        # 原始尺寸显示区域
        self.original_size_label = ttk.Label(
            self.size_control_frame,
            text="原始尺寸: 等待选择图片...",
            foreground="#CC0000"
        )
        self.original_size_label.pack(fill=tk.X, pady=(0, self.scale(0.5 * self.base_unit)))

        # 尺寸单位选择区域
        self.unit_subframe = ttk.Frame(self.size_control_frame)
        self.unit_subframe.pack(fill=tk.X, pady=(0, self.scale(0.5 * self.base_unit)))

        ttk.Label(self.unit_subframe, text="单位:").pack(side=tk.LEFT, padx=(0, self.scale(1.0 * self.base_unit)))
        self.unit_var = tk.StringVar(value="pixel")

        for unit, text in [("pixel", "像素"), ("cm", "厘米"), ("inch", "英寸")]:
            rb = ttk.Radiobutton(
                self.unit_subframe,
                text=text,
                variable=self.unit_var,
                value=unit,
                command=self.update_size_display,
                state="disabled",
                bootstyle="danger-round-toggle"
            )
            rb.pack(side=tk.LEFT, padx=(0, self.scale(1.0 * self.base_unit)))

        # 尺寸输入区域
        self.size_input_frame = ttk.Frame(self.size_control_frame)
        self.size_input_frame.pack(fill=tk.X, pady=(self.scale(0.5 * self.base_unit), 0))

        # 在尺寸输入区域下方添加提示标签
        self.size_hint_label = ttk.Label(
            self.size_input_frame,
            text="",
            style="info.TLabel"
        )
        self.size_hint_label.pack(side=tk.BOTTOM, pady=(self.scale(0.5 * self.base_unit), 0))

        # 宽度输入
        self.width_container = ttk.Frame(self.size_input_frame)
        self.width_container.pack(side=tk.LEFT, padx=(0, self.large_padx))
        ttk.Label(self.width_container, text="宽度:").pack(side=tk.LEFT)
        self.width_var = tk.StringVar()
        self.width_entry = ttk.Entry(
            self.width_container,
            textvariable=self.width_var,
            width=self.small_entry_width,
            state="disabled"  # 默认禁用
        )
        self.width_entry.pack(side=tk.LEFT, padx=self.small_padx)

        # 高度输入
        self.height_container = ttk.Frame(self.size_input_frame)
        self.height_container.pack(side=tk.LEFT)
        ttk.Label(self.height_container, text="高度:").pack(side=tk.LEFT)
        self.height_var = tk.StringVar()
        self.height_entry = ttk.Entry(
            self.height_container,
            textvariable=self.height_var,
            width=self.small_entry_width,
            state="disabled"  # 默认禁用
        )
        self.height_entry.pack(side=tk.LEFT, padx=self.small_padx)

        # 还原按钮
        self.restore_size_btn = ttk.Button(
            self.size_input_frame,
            text="还原原图尺寸",
            command=self.restore_original_size,
            state="disabled",
            bootstyle="danger"  # 使用danger主题颜色
        )
        self.restore_size_btn.pack(side=tk.RIGHT, padx=(0, self.medium_padx))

        # 在__init__方法中，在尺寸输入区域添加保持比例的复选框
        # 在size_input_frame创建后添加以下代码
        self.maintain_ratio = tk.BooleanVar(value=True)  # 默认开启保持比例
        self.ratio_checkbox = ttk.Checkbutton(
            self.size_input_frame,
            text="保持原图比例",
            variable=self.maintain_ratio,
            command=self.on_ratio_change,  # 添加命令回调
            state="disabled",
            bootstyle="danger-round-toggle"
        )
        self.ratio_checkbox.pack(side=tk.LEFT, padx=self.large_padx)

        # 添加批量保持比例的复选框
        self.batch_maintain_ratio = tk.BooleanVar(value=False)  # 默认关闭批量保持比例
        self.batch_ratio_checkbox = ttk.Checkbutton(
            self.size_input_frame,
            text="批量保持比例",
            variable=self.batch_maintain_ratio,
            state="disabled",
            bootstyle="danger-round-toggle",
            command=self.on_batch_ratio_changed
        )
        self.batch_ratio_checkbox.pack(side=tk.LEFT, padx=self.large_padx)

        # 尺寸预览
        self.preview_frame = ttk.LabelFrame(self.params_frame, text="像素尺寸预览",
                                            padding=str(self.scale(1.0 * self.base_unit)))
        self.preview_frame.pack(fill=tk.X, pady=(0, self.scale(1.0 * self.base_unit)))

        self.preview_label = ttk.Label(self.preview_frame, text="")
        self.preview_label.pack(fill=tk.X)

        # 添加以下代码来跟踪变量变化
        # 在 __init__ 方法中：
        self.width_var.trace_add("write", self.update_size_preview)
        self.height_var.trace_add("write", self.update_size_preview)
        self.dpi_var.trace_add("write", self.update_size_preview)
        # 添加对宽度和高度变量的跟踪，用于保持比例计算
        self.width_var.trace_add("write", self.calculate_proportional_size)
        self.height_var.trace_add("write", self.calculate_proportional_size)
        # 添加对宽度和高度变量的跟踪，用于在厘米/英寸单位下调整DPI
        self.width_var.trace_add("write", self.adjust_dpi_for_size_change)
        self.height_var.trace_add("write", self.adjust_dpi_for_size_change)
        # 添加DPI变化的跟踪，用于在像素模式下保持物理尺寸不变
        self.dpi_var.trace_add("write", self.adjust_size_for_dpi_change)

        # 为尺寸变量添加跟踪器，用于更新批量保持比例模式下的输入框状态
        self.width_var.trace_add("write", self.update_batch_ratio_state)
        self.height_var.trace_add("write", self.update_batch_ratio_state)

        # 为单位变量添加跟踪器，用于标准尺寸模式下的单位转换
        self.unit_var.trace_add("write", self.on_unit_change_for_standard_size)

        # 格式设置
        self.format_frame = ttk.LabelFrame(self.params_frame, text="输出格式设置",
                                           padding=str(self.scale(1.0 * self.base_unit)))
        self.format_frame.pack(fill=tk.X, pady=(0, self.scale(1.0 * self.base_unit)))

        # 创建一个容器来放置所有格式选项
        format_options_frame = ttk.Frame(self.format_frame)
        format_options_frame.pack(fill=tk.X)

        # 标签和单选按钮放在左边
        ttk.Label(format_options_frame, text="输出格式:").pack(side=tk.LEFT, padx=(0, self.scale(1.0 * self.base_unit)))
        self.format_var = tk.StringVar(value="original")

        self.format_original = ttk.Radiobutton(
            format_options_frame,
            text="保持原格式",
            variable=self.format_var,
            value="original",
            command=self.update_quality_visibility,
            bootstyle="danger-round-toggle"
        )
        self.format_original.pack(side=tk.LEFT, padx=(0, self.scale(1.0 * self.base_unit)))

        self.format_jpg = ttk.Radiobutton(
            format_options_frame,
            text="JPG",
            variable=self.format_var,
            value="jpg",
            command=self.update_quality_visibility,
            bootstyle="danger-round-toggle"
        )
        self.format_jpg.pack(side=tk.LEFT, padx=(0, self.scale(1.0 * self.base_unit)))

        self.format_png = ttk.Radiobutton(
            format_options_frame,
            text="PNG",
            variable=self.format_var,
            value="png",
            command=self.update_quality_visibility,
            bootstyle="danger-round-toggle"
        )
        self.format_png.pack(side=tk.LEFT)

        # JPG质量设置 (滑块形式，默认隐藏)
        self.quality_frame = ttk.Frame(self.format_frame)
        # 注意：这里不 pack，其可见性由 update_quality_visibility 控制

        ttk.Label(self.quality_frame, text="JPG质量:").pack(side=tk.LEFT, padx=(0, self.small_padx))

        # 用于显示数值 (0-12) 的标签
        self.quality_value_label = ttk.Label(self.quality_frame, text="10", width=3)  # 默认值 10
        self.quality_value_label.pack(side=tk.LEFT, padx=(0, self.small_padx))

        # 滑块变量
        self.quality_slider_var = tk.IntVar(value=10)  # 默认质量 10

        # 滑块 (Scale) 控件
        self.quality_slider = ttk.Scale(
            self.quality_frame,
            from_=0,
            to=12,
            orient=tk.HORIZONTAL,
            variable=self.quality_slider_var,
            command=self.update_quality_label_chinese  # 调用中文更新方法
        )
        self.quality_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=self.small_padx)

        # 用于显示描述 ("低", "中", "高", "最佳") 的标签
        self.quality_description_label = ttk.Label(self.quality_frame, text="高", width=4, anchor="w")  # 默认描述 "高"
        self.quality_description_label.pack(side=tk.LEFT, padx=(self.small_padx, 0))

        # --- 添加新的 Canvas 用于自定义进度条 ---
        self.progress_canvas_height = self.scale(1.4 * self.base_unit)  # 设置 Canvas 高度
        self.progress_canvas = tk.Canvas(
            self.main_frame,
            height=self.progress_canvas_height,
            bg=self.progress_bg_color,  # 设置默认背景色
            highlightthickness=0  # 移除边框
        )
        self.progress_canvas.pack(fill=tk.X, pady=(0, self.small_padding))
        # 移除之前的 update_idletasks 和直接调用
        # self.root.update_idletasks()
        # self.update_progress_display(0, 0)
        # 修改：延迟初始绘制
        self.root.after(100, lambda: self.update_progress_display(0, 0))

        # 创建按钮容器框架
        self.button_frame = ttk.Frame(self.main_frame)
        self.button_frame.pack(pady=(0, self.small_padding))

        # 开始按钮 (放入按钮容器)
        self.start_btn = ttk.Button(
            self.button_frame,  # 修改父控件为 button_frame
            text="开始处理",
            command=self.start_threaded_process,  # 使用多线程版本
            bootstyle="danger"
        )
        self.start_btn.pack(side=tk.LEFT, padx=(0, self.small_padding))  # 并排放置，添加右边距

        # 添加取消按钮 (放入按钮容器)
        self.cancel_btn = ttk.Button(
            self.button_frame,  # 修改父控件为 button_frame
            text="取消处理",
            command=self.cancel_processing_task,
            bootstyle="warning",  # 可以选择不同样式
            state=tk.DISABLED  # 初始状态为禁用
        )
        self.cancel_btn.pack(side=tk.LEFT)  # 并排放置

        # --- 初始化后台处理相关属性 ---
        self.processing_thread = None
        self.stop_event = threading.Event()  # 用于线程通信

        # 设置滚动条 - 只保留canvas，移除scrollbar
        self.main_canvas.pack(side="left", fill="both", expand=True)

        self.selected_files = []
        
        # 添加标志，避免在程序启动时显示RAW文件提示
        self._show_raw_dialog = False
        self.root.after(1000, lambda: setattr(self, '_show_raw_dialog', True))
        
        # 添加RAW相关的属性
        self.analysis_complete = False
        self.analysis_progress = 0
        self.current_analyzing_file = ""
        self.analysis_cancelled = False
        # 添加新的状态跟踪变量
        self.size_modified = False  # 跟踪用户是否修改了尺寸
        self.original_width = None  # 存储原始宽度
        self.original_height = None  # 存储原始高度
        self.all_images_info = []  # 存储所有图片的信息
        self.original_dpi = None  # 存储原始DPI
        self.last_used_dpi = None  # 存储上一次使用的DPI值，用于计算尺寸变化

        # 修改宽度和高度输入框的跟踪
        self.width_var.trace_add("write", self.on_size_modified)
        self.height_var.trace_add("write", self.on_size_modified)
        self.update_size_preview()

        # 添加路径记忆变量，用于记录上次选择的路径
        self.last_source_path = None  # 记录上次选择的源路径
        self.last_dest_path = None  # 记录上次选择的目标路径

        # 添加窗口高度记忆变量，用于在切换格式时恢复窗口高度
        self.original_window_height = None  # 记录切换到JPG前的窗口高度

        # 在初始化完成后执行额外的设置
        self.root.after(10, self.__init_complete__)

        # 添加变量来标记用户输入与程序自动计算
        self.user_input_width = False
        self.user_input_height = False
        self.user_input_dpi = False

        # 用于跟踪哪个字段最后被修改
        self.last_modified_field = None

        # 更新字体尺寸
        self.update_font_sizes()

        # ----- 开始添加多线程方法 (纯添加) -----

        self.processing_queue = queue.Queue()
        self.is_processing = False
        self.cancel_processing = False

        self._block_auto_update_size = True  # 新增：阻止自动同步宽高输入框
        self.root.after(800, lambda: setattr(self, '_block_auto_update_size', False))

    def scale(self, value):
        """根据屏幕尺寸缩放UI元素尺寸"""
        # 使用屏幕尺寸计算的缩放因子来缩放值
        return int(value * self.scale_factor)

    def is_raw_file(self, file_path):
        """判断文件是否为RAW格式"""
        # 更完整的RAW格式扩展名列表，基于LibRaw支持的格式
        raw_extensions = {
            # Canon
            '.cr2', '.cr3', '.crw',
            # Nikon
            '.nef', '.nrw',
            # Sony
            '.arw', '.sr2', '.srf',
            # Olympus
            '.orf',
            # Panasonic
            '.rw2', '.raw',
            # Pentax
            '.pef', '.ptx',
            # Samsung
            '.srw',
            # Fujifilm
            '.raf',
            # Hasselblad
            '.3fr', '.fff',
            # Phase One
            '.iiq',
            # Kodak
            '.k25', '.kdc',
            # Mamiya
            '.mef', '.mfw',
            # Leaf
            '.mos',
            # Minolta
            '.mrw',
            # Sigma
            '.x3f',
            # Epson
            '.erf',
            # Casio
            '.bay',
            # Ricoh/Adobe
            '.dng',  # Adobe DNG (通用格式)
            # 其他格式
            '.pxn', '.r3d', '.rwl', '.cap', '.tif', '.dcs', '.dcr', '.drf', '.ers'
        }
        file_ext = Path(file_path).suffix.lower()
        return file_ext in raw_extensions

    def open_image_file(self, file_path, preview_mode=False):
        """统一的图像文件打开方法，支持普通格式和RAW格式"""
        if self.is_raw_file(file_path):
            try:
                return load_raw_image(file_path, preview_mode=preview_mode)
            except Exception as e:
                # 如果RAW处理失败，尝试作为普通图像打开（某些RAW文件可能有嵌入的JPEG预览）
                try:
                    print(f"RAW处理失败，尝试作为普通图像打开: {file_path}")
                    return safe_open_image(file_path)
                except:
                    # 如果都失败了，抛出原始的RAW错误
                    raise e
        else:
            return safe_open_image(file_path)

    def skip_problematic_file(self, file_path, error_msg):
        """跳过有问题的文件并记录错误"""
        print(f"跳过文件: {file_path}")
        print(f"错误原因: {error_msg}")

        # 可以在这里添加错误日志记录
        error_log_path = Path(self.dest_path.get()) / "处理错误日志.txt"
        try:
            with open(error_log_path, 'a', encoding='utf-8') as f:
                from datetime import datetime
                timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                f.write(f"[{timestamp}] 跳过文件: {file_path}\n")
                f.write(f"错误原因: {error_msg}\n")
                f.write("-" * 50 + "\n")
        except:
            pass  # 如果无法写入日志也不要影响主程序

    def __init_complete__(self):
        """完成初始化后的操作"""
        # 绑定文件列表选择事件
        self.files_list.bind("<<TreeviewSelect>>", self.on_file_selected)

        # 执行初始窗口尺寸更新
        self.root.after(100, self.on_window_resize, None)

        # 触发第二次更新，确保在所有控件渲染后更新尺寸
        self.root.after(500, self.on_window_resize, None)

        # 在窗口大小调整后，将窗口居中显示
        self.root.after(600, self.center_window)

        # 确保路径输入框和文件列表填充可用空间
        if hasattr(self, 'source_entry'):
            self.source_entry.configure(width=0)  # 将宽度设为0，让它自动扩展填充可用空间

        if hasattr(self, 'dest_entry'):
            self.dest_entry.configure(width=0)  # 将宽度设为0，让它自动扩展填充可用空间

        # 自动调整窗口高度
        self.adjust_window_height()

    def update_window_dimensions(self, event=None):
        """更新窗口尺寸以适应内容"""
        # 这个方法保留但不再用于主要的窗口调整逻辑
        # 现在窗口调整主要在main函数中处理
        pass

    def calculate_window_borders(self):
        """计算窗口边框的实际尺寸，使用缩放系数确保比例一致"""
        try:
            # 保存当前几何信息
            current_geometry = self.root.geometry()

            # 获取内容区域尺寸
            content_width = self.scrollable_frame.winfo_reqwidth()
            content_height = self.scrollable_frame.winfo_reqheight()

            # 获取窗口实际尺寸
            self.root.update_idletasks()
            window_width = self.root.winfo_width()
            window_height = self.root.winfo_height()

            # 计算边框尺寸（水平和垂直）
            self.horiz_border = max(0, window_width - content_width)
            self.vert_border = max(0, window_height - content_height)

            # 如果计算结果不合理，使用缩放值
            if self.horiz_border > self.scale(10 * self.base_unit) or self.vert_border > self.scale(
                    10 * self.base_unit):
                self.horiz_border = self.scale(0.5 * self.base_unit)
                self.vert_border = self.scale(0.5 * self.base_unit)

        except Exception as e:
            print(f"计算窗口边框时发生错误: {e}")
            # 默认边框尺寸
            self.horiz_border = self.scale(0.5 * self.base_unit)
            self.vert_border = self.scale(0.5 * self.base_unit)

    def final_size_adjustment(self):
        """调整窗口大小以适应内容"""
        try:
            # 确保所有组件都已更新完毕
            self.root.update_idletasks()

            # 获取实际内容尺寸
            content_width = self.scrollable_frame.winfo_reqwidth()
            content_height = self.scrollable_frame.winfo_reqheight()

            # 添加边距
            padding = self.scale(0.5 * self.base_unit)
            final_width = content_width + padding
            final_height = content_height + padding

            # 确保窗口不会超出屏幕
            max_width = int(self.screen_width * 0.95)
            max_height = int(self.screen_height * 0.9)

            if final_width > max_width:
                final_width = max_width
            if final_height > max_height:
                final_height = max_height

            # 设置窗口大小
            self.root.geometry(f"{final_width}x{final_height}")

            # 更新滚动区域
            self.main_canvas.configure(scrollregion=self.main_canvas.bbox("all"))

        except Exception as e:
            print(f"调整窗口大小时发生错误: {e}")
            # 出错时使用常规的窗口尺寸更新方法
            self.update_window_dimensions()

    def update_source_frame(self):
        """根据选择的模式更新源框架的内容"""
        # 清除文件列表
        self.files_list.delete(*self.files_list.get_children())
        self.selected_files = []
        self.source_path.set("")
        self.all_images_info = []
        self.original_width = None
        self.original_height = None
        self.original_dpi = None
        self.size_modified = False

        # 重置保持比例设置
        self.maintain_ratio.set(False)
        self.batch_maintain_ratio.set(False)  # 确保批量保持比例也被重置
        self.ratio_checkbox.configure(state="disabled")
        self.batch_ratio_checkbox.configure(state="disabled")  # 禁用批量保持比例复选框

        # 重置尺寸和DPI相关的显示
        self.original_size_label.configure(text="原始尺寸: 等待选择图片...")
        self.width_var.set("")
        self.height_var.set("")
        self.dpi_var.set("")  # 添加这一行来清空DPI值
        self.preview_label.configure(text="")

        # 清除文件路径显示
        self.source_path.set("")  # 清除文件夹路径输入框
        self.files_list.delete(*self.files_list.get_children())  # 清除文件列表的所有内容

        # *** 新增：清空目标路径 ***
        self.dest_path.set("")  # 在切换模式时清空导出位置

        # 根据模式显示不同的控件
        if self.mode_var.get() == "folder":
            # 文件夹模式
            self.files_list.pack_forget()
            # 修改这一行：设置宽度为0，让输入框填充可用空间
            self.source_entry.configure(width=0)
            self.source_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
            self.source_frame.configure(text="加载文件路径")
        else:
            # 选择图片模式
            self.source_entry.pack_forget()
            # 修改这里: 确保files_list的包装方式与source_entry一致
            self.files_list.pack(fill=tk.X, expand=True, padx=(0, 10))  # 添加padx确保与浏览按钮的间距一致
            self.source_frame.configure(text="加载图片路径")

        # 确保source_frame和dest_frame的大小一致
        self.source_frame.update_idletasks()  # 强制更新布局
        frame_width = max(self.source_frame.winfo_width(), self.dest_frame.winfo_width())
        self.source_frame.configure(width=frame_width)
        self.dest_frame.configure(width=frame_width)

        # 更新窗口大小以适应内容变化
        self.root.update_idletasks()

        # 获取内容的实际大小
        required_width = self.main_frame.winfo_reqwidth()
        required_height = self.main_frame.winfo_reqheight()

        # 调整窗口大小以适应内容
        current_width = self.root.winfo_width()
        # 保持当前宽度，但调整高度
        self.root.geometry(f"{current_width}x{required_height}")

        # 触发窗口大小调整事件
        self.on_window_resize(None)

        self._block_auto_update_size = True
        self.root.after(800, lambda: setattr(self, '_block_auto_update_size', False))

    def select_dest(self):
        # 如果有上次的目标路径记录，使用其父目录作为初始目录
        initial_dir = None
        if self.last_dest_path:
            initial_dir = os.path.dirname(self.last_dest_path)  # 获取父目录

        path = filedialog.askdirectory(initialdir=initial_dir)
        if path:
            # 记录当前选择的路径，供下次使用
            self.last_dest_path = path
            self.dest_path.set(path)

            # 更新目标路径输入框的样式
            if hasattr(self, 'dest_entry'):
                # 设置宽度为0，让输入框填充可用空间
                self.dest_entry.configure(width=0)

    def update_quality_visibility(self):
        """根据选择的格式显示或隐藏JPG质量设置，并精确控制窗口高度"""
        current_format = self.format_var.get()

        # 确保 quality_frame 存在
        if not hasattr(self, 'quality_frame'):
            print("错误：quality_frame 未初始化")
            return

        if current_format == "jpg":
            # 切换到JPG格式
            # **显示质量设置框**
            # 使用 pack 将其放置在格式框架内，进度条之前 (假设进度条在 main_frame)
            self.quality_frame.pack(fill=tk.X, pady=(self.scale(1.0 * self.base_unit), 0))
            # 更新标签以防万一
            self.update_quality_label_chinese(self.quality_slider_var.get())
        else:
            # 切换到其他格式
            # **隐藏质量设置框**
            self.quality_frame.pack_forget()

        # 自动调整窗口高度 (延迟一点执行确保布局更新)
        self.root.after(50, self.adjust_window_height)

    def update_format_options_for_raw(self, has_raw_files):
        """根据是否包含RAW文件来更新格式选项"""
        if has_raw_files:
            # 禁用"保持原格式"选项
            self.format_original.config(state='disabled')

            # 如果当前选择的是"保持原格式"，自动切换到JPG
            if self.format_var.get() == "original":
                self.format_var.set("jpg")
                self.update_quality_visibility()

            # 仅打印一次提示信息，避免重复
            if not hasattr(self, '_raw_print_done') or not self._raw_print_done:
                print("检测到RAW文件，已禁用'保持原格式'选项，RAW文件将转换为JPG或PNG格式")
                self._raw_print_done = True

            # 只在用户主动选择文件时显示提示对话框
            if hasattr(self, '_show_raw_dialog') and self._show_raw_dialog:
                messagebox.showinfo(
                    "RAW文件检测",
                    "检测到RAW格式文件！\n\n"
                    "• '保持原格式'选项已被禁用\n"
                    "• RAW文件将转换为JPG或PNG格式\n"
                    "• 已自动选择JPG格式\n"
                    "• 您可以根据需要选择PNG格式"
                )
        else:
            # 启用"保持原格式"选项
            self.format_original.config(state='normal')

    def get_output_extension(self, original_path):
        format_choice = self.format_var.get()
        if format_choice == "original":
            # 对于HEIC格式，由于不是所有程序都支持，建议转换为JPG
            if original_path.suffix.lower() in ['.heic', '.heif'] and not HEIC_SUPPORTED:
                return ".jpg"
            return original_path.suffix
        elif format_choice == "jpg":
            return ".jpg"
        else:  # png
            return ".png"

    def create_size_controls(self):
        """创建带有明确控制的尺寸设置界面"""
        # 尺寸控制框架
        self.size_control_frame = ttk.LabelFrame(self.params_frame, text="尺寸设置", padding="10")
        self.size_control_frame.pack(fill=tk.X, pady=(0, 10))

        # 类似DPI的控制方式
        self.size_enabled = tk.BooleanVar(value=False)
        self.size_checkbox = ttk.Checkbutton(
            self.size_control_frame,
            text="修改尺寸(当不勾选时，保持原图尺寸不变)",
            variable=self.size_enabled,
            command=self.update_size_state,
            bootstyle="danger-round-toggle"
        )
        self.size_checkbox.pack(fill=tk.X, pady=(0, 5))

        # 显示原始尺寸的标签
        self.original_size_label = ttk.Label(
            self.size_control_frame,
            text="原始尺寸: 等待选择图片...",
            style="info.TLabel"
        )
        self.original_size_label.pack(fill=tk.X, pady=(0, 5))

        # 尺寸输入区域
        self.size_input_frame = ttk.Frame(self.size_control_frame)
        self.size_input_frame.pack(fill=tk.X, pady=(0, 5))

        # 宽度和高度输入框 (默认禁用)
        ttk.Label(self.size_input_frame, text="宽度:").pack(side=tk.LEFT)
        self.width_entry = ttk.Entry(
            self.size_input_frame,
            textvariable=self.width_var,
            width=10,
            state="disabled"
        )
        self.width_entry.pack(side=tk.LEFT, padx=(5, 20))

        ttk.Label(self.size_input_frame, text="高度:").pack(side=tk.LEFT)
        self.height_entry = ttk.Entry(
            self.size_input_frame,
            textvariable=self.height_var,
            width=10,
            state="disabled"
        )
        self.height_entry.pack(side=tk.LEFT, padx=5)

        # 还原按钮
        self.restore_size_btn = ttk.Button(
            self.size_input_frame,
            text="还原原始尺寸",
            command=self.restore_original_size,
            state="disabled"
        )
        self.restore_size_btn.pack(side=tk.RIGHT, padx=(20, 0))

        # 添加验证及焦点事件来跟踪用户输入
        def on_width_focus_in(event):
            self.user_input_width = True

        def on_height_focus_in(event):
            self.user_input_height = True

        def on_dpi_focus_in(event):
            self.user_input_dpi = True

        def on_width_key(event):
            # 设置用户输入标志
            self.user_input_width = True
            # 延迟重置高度标志，确保其他值能够自动调整
            if self.maintain_ratio.get():
                self.root.after(50, lambda: setattr(self, 'user_input_height', False))
            # 输入完成后检查计算
            if event.keysym in ('Return', 'Tab'):
                self.root.after(100, self.verify_calculations)

        def on_height_key(event):
            # 设置用户输入标志
            self.user_input_height = True
            # 延迟重置宽度标志，确保其他值能够自动调整
            if self.maintain_ratio.get():
                self.root.after(50, lambda: setattr(self, 'user_input_width', False))
            # 输入完成后检查计算
            if event.keysym in ('Return', 'Tab'):
                self.root.after(100, self.verify_calculations)

        def on_dpi_key(event):
            # 设置用户输入标志
            self.user_input_dpi = True
            # 延迟重置宽度和高度标志，确保其他值能够自动调整
            if self.maintain_ratio.get():
                self.root.after(50, lambda: setattr(self, 'user_input_width', False))
                self.root.after(50, lambda: setattr(self, 'user_input_height', False))
            # 输入完成后检查计算
            if event.keysym in ('Return', 'Tab'):
                self.root.after(100, self.verify_calculations)

        def on_focus_out(event):
            # 点击其他地方时，只有在必要时才重置标志
            self.root.after(200, self.delayed_reset_flags)
            # 当失去焦点时检查计算
            self.root.after(100, self.verify_calculations)

        # 绑定焦点事件到输入框
        self.width_entry.bind("<FocusIn>", on_width_focus_in)
        self.height_entry.bind("<FocusIn>", on_height_focus_in)
        self.dpi_entry.bind("<FocusIn>", on_dpi_focus_in)

        # 绑定按键事件，确保用户输入时标记被正确设置
        self.width_entry.bind("<Key>", on_width_key)
        self.height_entry.bind("<Key>", on_height_key)
        self.dpi_entry.bind("<Key>", on_dpi_key)

        # 绑定失去焦点事件
        self.width_entry.bind("<FocusOut>", on_focus_out)
        self.height_entry.bind("<FocusOut>", on_focus_out)
        self.dpi_entry.bind("<FocusOut>", on_focus_out)

    def select_source(self):
        """
        选择源文件或文件夹的方法。
        处理单个文件和批量文件的选择，同时完成图片信息分析和界面更新。
        """
        # 清除旧的DPI和尺寸状态
        self.dpi_enabled.set(False)
        self.size_enabled.set(False)

        # 清除旧的状态
        self.maintain_ratio.set(False)
        self.batch_maintain_ratio.set(False)  # 确保批量保持比例也被重置
        self.ratio_checkbox.configure(state="disabled")
        self.batch_ratio_checkbox.configure(state="disabled")  # 禁用批量保持比例复选框
        self.files_list.delete(*self.files_list.get_children())
        self.selected_files = []
        self.all_images_info = []

        # 重置修改标志，确保新选择的图片会正确显示
        self.size_modified = False

        # 重置标准尺寸选择
        if hasattr(self, 'standard_size_var'):
            self.standard_size_var.set("自定义")
            self.standard_size_applied = False

        # 重置宽高输入框状态，确保新加载的图片不受之前操作的影响
        if self.size_enabled.get():
            self.width_entry.configure(state="normal")
            self.height_entry.configure(state="normal")

        # 重置用户输入标志
        self.user_input_width = False
        self.user_input_height = False

        if self.mode_var.get() == "folder":
            # 文件夹模式处理
            initial_dir = None
            # 如果有上次的源路径记录，使用其父目录作为初始目录
            if self.last_source_path:
                initial_dir = os.path.dirname(self.last_source_path)  # 获取父目录

            path = filedialog.askdirectory(initialdir=initial_dir)
            if path:
                # 记录当前选择的路径，供下次使用
                self.last_source_path = path

                # 新增路径规范化处理
                path = os.path.normpath(path)  # 标准化路径
                self.source_path.set(path)

                # 递归查找所有支持的图片文件（包括子文件夹）
                image_files = []
                # 普通图片格式
                common_formats = ['*.jpg', '*.jpeg', '*.png', '*.bmp']
                if HEIC_SUPPORTED:
                    common_formats.extend(['*.heic', '*.heif'])
                
                # 完整的RAW格式列表
                raw_formats = [
                    '*.cr2', '*.cr3', '*.crw',  # Canon
                    '*.nef', '*.nrw',  # Nikon
                    '*.arw', '*.sr2', '*.srf',  # Sony
                    '*.orf',  # Olympus
                    '*.rw2', '*.raw',  # Panasonic
                    '*.pef', '*.ptx',  # Pentax
                    '*.srw',  # Samsung
                    '*.raf',  # Fujifilm
                    '*.3fr', '*.fff',  # Hasselblad
                    '*.iiq',  # Phase One
                    '*.k25', '*.kdc',  # Kodak
                    '*.mef', '*.mfw',  # Mamiya
                    '*.mos',  # Leaf
                    '*.mrw',  # Minolta
                    '*.x3f',  # Sigma
                    '*.erf',  # Epson
                    '*.bay',  # Casio
                    '*.dng',  # Adobe DNG
                    '*.pxn', '*.r3d', '*.rwl', '*.cap', '*.tif', '*.dcs', '*.dcr', '*.drf', '*.ers'  # 其他
                ]

                # 搜索所有支持的格式
                supported_exts = common_formats + raw_formats
                for ext in supported_exts:
                    image_files.extend(Path(path).rglob(ext))
                # 如果找到了图片文件
                if image_files:
                    self.selected_files = [str(f) for f in image_files]  # 保存文件列表

                    try:
                        # 分析所有图片，传入基础路径用于计算相对路径
                        self.analyze_images(self.selected_files, path)

                        # 读取第一张图片获取基础信息
                        with self.open_image_file(self.selected_files[0], preview_mode=True) as first_image:
                            # 获取考虑方向的尺寸
                            self.original_width, self.original_height = self.get_oriented_size(first_image)
                            self.original_dpi = first_image.info.get('dpi', (72, 72))[0]
                            # 初始化last_used_dpi为原始DPI值
                            self.last_used_dpi = float(self.original_dpi)

                        # 获取批量图片的统一性分析结果
                        dpi_value, uniform_width, uniform_height, uniform_dpi, uniform_size = self.check_images_uniformity()

                        # 根据图片尺寸统一性控制保持比例功能
                        if uniform_size or len(self.selected_files) == 1:
                            self.maintain_ratio.set(True)  # 启用保持比例
                            if self.size_enabled.get():  # 如果尺寸修改已启用
                                self.ratio_checkbox.configure(state="normal")
                        else:
                            self.maintain_ratio.set(False)  # 禁用保持比例
                            self.ratio_checkbox.configure(state="disabled")

                        # 更新DPI显示（无论是否启用DPI修改）
                        if uniform_dpi or len(self.selected_files) == 1:
                            self.dpi_var.set(str(int(dpi_value)))
                        else:
                            # 如果DPI不统一，清空DPI输入框
                            self.dpi_var.set("")

                        # 更新原始尺寸标签显示
                        if uniform_size:
                            self.original_size_label.configure(
                                text=f"原始尺寸: {uniform_width} × {uniform_height} 像素"
                            )
                            # 始终更新输入框以显示当前图片尺寸
                            if self.unit_var.get() == "pixel":
                                self.width_var.set(str(uniform_width))
                                self.height_var.set(str(uniform_height))
                            else:
                                # 转换为当前选择的单位
                                current_dpi = float(self.dpi_var.get()) if self.dpi_var.get() else 72.0
                                unit = self.unit_var.get()
                                if unit == "cm":
                                    width_cm = (uniform_width / current_dpi) * 2.54
                                    height_cm = (uniform_height / current_dpi) * 2.54
                                    self.width_var.set(f"{width_cm:.2f}")
                                    self.height_var.set(f"{height_cm:.2f}")
                                elif unit == "inch":
                                    width_inch = uniform_width / current_dpi
                                    height_inch = uniform_height / current_dpi
                                    self.width_var.set(f"{width_inch:.2f}")
                                    self.height_var.set(f"{height_inch:.2f}")
                        else:
                            self.original_size_label.configure(
                                text="原始尺寸: 批量图片尺寸均不同，无法显示数值，您可以填入具体数值批量修改宽或高"
                            )
                            # 清空输入框，无论是否勾选了修改尺寸选项
                            self.width_var.set("")
                            self.height_var.set("")

                        # 显示文件列表
                        for file in self.selected_files:
                            self.files_list.insert("", "end", values=(file,))

                        # 重置尺寸修改标志
                        self.size_modified = False

                        # 更新预览显示
                        self.update_size_preview()

                        # 更新尺寸相关控件的状态
                        self.update_size_state()

                        # 更新窗口大小以适应内容
                        self.root.update_idletasks()
                        self.on_window_resize(None)

                        self._block_auto_update_size = True
                        self.root.after(800, lambda: setattr(self, '_block_auto_update_size', False))

                    except Exception as e:
                        messagebox.showerror("错误", f"分析图片时发生错误:\n{str(e)}")
                        self.original_size_label.configure(text="原始尺寸: 读取错误")
                else:
                    messagebox.showwarning("警告", "所选文件夹中未找到支持的图片文件")
                    self.original_size_label.configure(text="原始尺寸: 未找到图片文件")
                    self.selected_files = []
        else:
            # 文件选择模式的处理
            initial_dir = None
            # 如果有上次的源路径记录，使用其父目录作为初始目录
            if self.last_source_path:
                # 如果上次选择的是文件，获取其所在目录；如果是目录，直接使用
                if os.path.isfile(self.last_source_path):
                    initial_dir = os.path.dirname(self.last_source_path)
                else:
                    initial_dir = self.last_source_path

            # 构建支持的文件类型字符串
            common_formats = "*.jpg *.jpeg *.png *.bmp"
            if HEIC_SUPPORTED:
                common_formats += " *.heic *.heif"
            
            # RAW格式
            raw_formats = "*.cr2 *.cr3 *.crw *.nef *.nrw *.arw *.sr2 *.srf *.orf *.rw2 *.raw *.pef *.ptx *.srw *.raf *.3fr *.fff *.iiq *.k25 *.kdc *.mef *.mfw *.mos *.mrw *.x3f *.erf *.bay *.dng *.pxn *.r3d *.rwl *.cap *.tif *.dcs *.dcr *.drf *.ers"
            
            supported_formats = common_formats + " " + raw_formats

            files = filedialog.askopenfilenames(
                filetypes=[
                    ("图片文件", supported_formats),
                    ("所有文件", "*.*")
                ],
                initialdir=initial_dir
            )
            if files:
                # 如果选择了文件，记录第一个文件的路径
                if files:
                    self.last_source_path = files[0]

                # 更新选中的文件列表
                self.selected_files = list(files)
                self.files_list.delete(*self.files_list.get_children())

                # 分析所有选中的图片
                self.analyze_images(self.selected_files)

                try:
                    # 读取第一张图片作为参考
                    with self.open_image_file(files[0], preview_mode=True) as first_image:
                        # 获取并保存原始尺寸（考虑方向）
                        self.original_width, self.original_height = self.get_oriented_size(first_image)
                        self.original_dpi = first_image.info.get('dpi', (72, 72))[0]
                        # 初始化last_used_dpi为原始DPI值
                        self.last_used_dpi = float(self.original_dpi)

                        # 获取统一性检查结果
                        dpi_value, uniform_width, uniform_height, uniform_dpi, uniform_size = self.check_images_uniformity()

                        # 更新DPI显示（无论是否启用DPI修改）
                        if uniform_dpi or len(self.selected_files) == 1:
                            self.dpi_var.set(str(int(dpi_value)))
                        else:
                            # 如果DPI不统一，清空DPI输入框
                            self.dpi_var.set("")

                        # 根据图片尺寸统一性控制保持比例功能
                        if uniform_size or len(self.selected_files) == 1:
                            self.maintain_ratio.set(True)  # 启用保持比例
                            if self.size_enabled.get():  # 如果尺寸修改已启用
                                self.ratio_checkbox.configure(state="normal")
                        else:
                            self.maintain_ratio.set(False)  # 禁用保持比例
                            self.ratio_checkbox.configure(state="disabled")

                        # 更新原始尺寸标签显示
                        if uniform_size:
                            self.original_size_label.configure(
                                text=f"原始尺寸: {uniform_width} × {uniform_height} 像素"
                            )
                            # 始终更新尺寸输入框以显示当前图片尺寸
                            if self.unit_var.get() == "pixel":
                                self.width_var.set(str(uniform_width))
                                self.height_var.set(str(uniform_height))
                            else:
                                # 转换为当前选择的单位
                                current_dpi = float(self.dpi_var.get()) if self.dpi_var.get() else 72.0
                                unit = self.unit_var.get()
                                if unit == "cm":
                                    width_cm = (uniform_width / current_dpi) * 2.54
                                    height_cm = (uniform_height / current_dpi) * 2.54
                                    self.width_var.set(f"{width_cm:.2f}")
                                    self.height_var.set(f"{height_cm:.2f}")
                                elif unit == "inch":
                                    width_inch = uniform_width / current_dpi
                                    height_inch = uniform_height / current_dpi
                                    self.width_var.set(f"{width_inch:.2f}")
                                    self.height_var.set(f"{height_inch:.2f}")
                        else:
                            self.original_size_label.configure(
                                text="原始尺寸: 批量图片尺寸均不同，无法显示数值，您可以填入具体数值批量修改宽或高"
                            )
                            # 清空输入框，无论是否勾选了修改尺寸选项
                            self.width_var.set("")
                            self.height_var.set("")

                        # 显示文件列表
                        for file in files:
                            self.files_list.insert("", "end", values=(file,))

                        # 重置尺寸修改标志
                        self.size_modified = False

                        # 更新预览显示
                        self.update_size_preview()

                        # 更新尺寸相关控件的状态
                        self.update_size_state()

                        # 更新窗口大小以适应内容
                        self.root.update_idletasks()
                        self.on_window_resize(None)

                except Exception as e:
                    messagebox.showerror("错误", f"读取图片时发生错误:\n{str(e)}")
                    self.original_size_label.configure(text="原始尺寸: 读取错误")

        # 强制开启DPI和尺寸修改按钮（每次加载图片后都自动开启）
        self.dpi_enabled.set(True)
        self.size_enabled.set(True)
        self.update_dpi_state()
        self.update_size_state()

    def on_file_selected(self, event):
        # 防止event为None导致的错误
        if event is None:
            return

        try:
            selected_item = self.files_list.selection()
            if not selected_item:
                return

            # 保存当前DPI启用状态，以便加载完成后恢复
            dpi_was_enabled = self.dpi_enabled.get()
            # 如果DPI修改已启用，则临时禁用它
            if dpi_was_enabled:
                self.dpi_enabled.set(False)

            # 获取选中文件的路径
            selected_file = self.files_list.item(selected_item[0])["values"][0]

            # 清除之前的修改标志，确保新选择的图片会正确显示
            self.size_modified = False

            # 读取选中的图片
            with safe_open_image(selected_file) as img:
                # 更新尺寸信息
                self.original_width, self.original_height = self.get_oriented_size(img)
                self.original_dpi = img.info.get('dpi', (72, 72))[0]
                # 初始化last_used_dpi为原始DPI值
                self.last_used_dpi = float(self.original_dpi)

                # 始终显示新图片的原始DPI值，无论是否启用DPI修改
                self.dpi_var.set(str(int(self.original_dpi)))

                # 更新原始尺寸标签
                self.original_size_label.configure(
                    text=f"原始尺寸: {self.original_width} × {self.original_height} 像素"
                )

                # 始终更新尺寸输入框以显示新图片的尺寸
                if self.unit_var.get() == "pixel":
                    self.width_var.set(str(self.original_width))
                    self.height_var.set(str(self.original_height))
                else:
                    # 转换为当前选择的单位
                    current_dpi = float(self.dpi_var.get()) if self.dpi_var.get() else 72.0
                    unit = self.unit_var.get()
                    if unit == "cm":
                        width_cm = (self.original_width / current_dpi) * 2.54
                        height_cm = (self.original_height / current_dpi) * 2.54
                        self.width_var.set(f"{width_cm:.2f}")
                        self.height_var.set(f"{height_cm:.2f}")
                    elif unit == "inch":
                        width_inch = self.original_width / current_dpi
                        height_inch = self.original_height / current_dpi
                        self.width_var.set(f"{width_inch:.2f}")
                        self.height_var.set(f"{height_inch:.2f}")

                # 启用保持比例选项（对单个图片）
                if self.size_enabled.get():
                    self.maintain_ratio.set(True)
                    self.ratio_checkbox.configure(state="normal")

                # 更新预览
                self.update_size_preview()

                # 更新所有图片信息列表，确保预览正确显示当前图片信息
                self.all_images_info = [{
                    'path': selected_file,
                    'dpi': self.original_dpi,
                    'width': self.original_width,
                    'height': self.original_height
                }]

                # 更新窗口大小以适应内容
                self.root.update_idletasks()
                self.on_window_resize(None)

            # 更新尺寸显示
            self.update_size_display()

            # 自动调整窗口高度
            self.adjust_window_height()

        except Exception as e:
            messagebox.showerror("错误", f"读取图片时发生错误:\n{str(e)}")
            self.original_size_label.configure(text="原始尺寸: 读取错误")

        # 如果DPI之前是启用的，则恢复其状态
        if dpi_was_enabled:
            self.dpi_enabled.set(True)
            self.update_dpi_state()  # 添加这行，确保DPI输入框状态正确更新

    def center_window(self):
        """将窗口居中于屏幕"""
        self.root.update_idletasks()  # 确保窗口尺寸已更新

        # 获取屏幕宽度和高度
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()

        # 获取窗口宽度和高度
        window_width = self.root.winfo_width()
        window_height = self.root.winfo_height()

        # 计算窗口应该居中的位置
        x = (screen_width - window_width) // 2
        y = (screen_height - window_height) // 2

        # 设置窗口位置
        self.root.geometry(f"+{x}+{y}")

    def on_window_resize(self, event):
        # 检查event是否为None或event.widget是否存在
        if event is None or not hasattr(event, 'widget'):
            return
        # 仅在事件是窗口大小变化且非程序初始化时处理
        if event.widget == self.root and self.root.winfo_width() > 1 and self.root.winfo_height() > 1:
            # 获取窗口的实际大小
            window_width = self.root.winfo_width()
            window_height = self.root.winfo_height()

            # 根据窗口与屏幕的比例计算当前缩放因子
            current_width_ratio = window_width / self.screen_width
            current_height_ratio = window_height / self.screen_height

            # 根据窗口尺寸的变化调整组件大小和布局
            # 仅在框架已初始化时进行处理
            if hasattr(self, 'main_frame'):
                # 更新滚动区域
                self.main_frame.update_idletasks()

                # 在这里可以添加更多根据当前窗口大小的自适应逻辑
                # 例如调整字体大小、控件间距等
                pass

    def ensure_entries_fill_space(self):
        """确保输入框填充可用空间"""
        # 此方法不再需要复杂的宽度计算
        # 因为我们已经使用了pack布局管理器并设置了fill=tk.X, expand=True

        # 只需确保输入框的width为0，让它们根据容器大小自动调整
        if hasattr(self, 'source_entry'):
            self.source_entry.configure(width=0)

        if hasattr(self, 'dest_entry'):
            self.dest_entry.configure(width=0)

        # 刷新布局
        if hasattr(self, 'source_frame'):
            self.source_frame.update_idletasks()

        if hasattr(self, 'dest_frame'):
            self.dest_frame.update_idletasks()

    def update_font_sizes(self):
        """更新应用程序中所有控件的字体大小"""
        # 基准字体大小
        base_font_size = 9

        # 根据缩放因子计算实际字体大小
        font_size = max(8, int(base_font_size * self.scale_factor))

        # 统一设置所有控件的基本字体
        self.style.configure('.', font=('Microsoft YaHei UI', font_size))

        # 设置标题字体大小（略大一点）
        title_font_size = max(10, int((base_font_size + 1) * self.scale_factor))
        self.style.configure('TLabelframe.Label', font=('Microsoft YaHei UI', title_font_size, 'bold'))

        # 确保所有按钮字体一致
        self.style.configure('TButton', font=('Microsoft YaHei UI', font_size))

        # 确保所有标签字体一致
        self.style.configure('TLabel', font=('Microsoft YaHei UI', font_size))

        # 确保所有输入框字体一致
        self.style.configure('TEntry', font=('Microsoft YaHei UI', font_size))

        # 确保复选框字体一致
        self.style.configure('TCheckbutton', font=('Microsoft YaHei UI', font_size))

        # 确保单选按钮字体一致
        self.style.configure('TRadiobutton', font=('Microsoft YaHei UI', font_size))

        # 确保组合框字体一致
        self.style.configure('TCombobox', font=('Microsoft YaHei UI', font_size))

    def adjust_window_height(self):
        """根据内容自动调整窗口高度"""
        # 确保所有子控件已更新
        self.root.update_idletasks()

        # 获取当前窗口宽度
        current_width = self.root.winfo_width()
        if current_width <= 1:  # 窗口尚未完全初始化
            current_width = self.window_width

        # 获取主框架需要的高度
        required_height = self.main_frame.winfo_reqheight()

        # 获取屏幕高度
        screen_height = self.root.winfo_screenheight()

        # 确保窗口高度不超过屏幕的90%
        final_height = min(required_height, int(screen_height * 0.9))

        # 应用新的窗口尺寸
        self.root.geometry(f"{current_width}x{final_height}")

    # ----- 开始添加多线程方法 (纯添加) -----


    # ----- 添加新的方法来更新中文质量标签 -----


    def scale(self, value):
        """根据屏幕尺寸缩放UI元素尺寸"""
        # 使用屏幕尺寸计算的缩放因子来缩放值
        return int(value * self.scale_factor)


    def show_heic_warning(self):
        """显示HEIC格式支持警告"""
        if messagebox.askyesno("HEIC格式支持",
                               "检测到您的系统尚未安装HEIC格式支持库。\n\n"
                               "要支持.heic和.heif格式，需要安装pillow-heif库。\n"
                               "是否现在查看安装说明？"):
            messagebox.showinfo("安装说明",
                                "请在命令提示符或终端中运行以下命令：\n\n"
                                "pip install pillow-heif\n\n"
                                "安装完成后重启软件即可支持HEIC格式。\n\n"
                                "注意：如果您使用的是打包的exe版本，\n"
                                "可能需要重新打包才能支持HEIC格式。")

